package jcu.sal.common.agents.netty;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

import jcu.sal.common.Response;
import jcu.sal.common.StreamID;
import jcu.sal.common.events.Event;

import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.frame.FrameDecoder;

/**
 * @author Marc Hammerton
 * 
 */
public class NettyClientMessageDecoder extends FrameDecoder {

	private short type = 0;
	private int headerLength = 0;
	private int bodyLength = 0;

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.jboss.netty.handler.codec.frame.FrameDecoder#decode(org.jboss.netty
	 * .channel.ChannelHandlerContext, org.jboss.netty.channel.Channel,
	 * org.jboss.netty.buffer.ChannelBuffer)
	 */
	@Override
	protected Object decode(ChannelHandlerContext chx, Channel channel,
			ChannelBuffer buffer) throws Exception {

		// Wait until at least the short header is available
		if (buffer.readableBytes() < NettyConstants.HEADER_LENGTH_FIXED_DATALENGTH) {
			return null;
		}
		this.type = buffer.getShort(buffer.readerIndex());

		switch (this.type) {
			case NettyConstants.RESP_SYNC_STRING:
			case NettyConstants.RESP_SYNC_STREAMID:
			case NettyConstants.RESP_ASYNC_RESPONSE:
			case NettyConstants.RESP_ASYNC_EVENT:
				if (buffer.readableBytes() < NettyConstants.HEADER_LENGTH_VAR_DATALENGTH) {
					return null;
				}
				this.bodyLength = buffer.getInt(buffer.readerIndex() + 2);
				this.headerLength = NettyConstants.HEADER_LENGTH_VAR_DATALENGTH;
				break;
			default:
				this.bodyLength = 0;
				this.headerLength = NettyConstants.HEADER_LENGTH_FIXED_DATALENGTH;
				break;
		}

		// Wait until the whole data is available.
		if (buffer.readableBytes() < this.bodyLength + this.headerLength) {
			return null;
		}

		// Skip the length field because we know it already.
		buffer.skipBytes(this.headerLength);

		NettyMessage message = new NettyMessage(type);
		switch (type) {
			case NettyConstants.RESP_SYNC_STRING:
				String text = decodeString(buffer);
				message.addData(text);
				break;
			case NettyConstants.RESP_SYNC_STREAMID:
				StreamID streamId = decodeStreamID(buffer);
				message.addData(streamId);
				break;
			case NettyConstants.RESP_ASYNC_RESPONSE:
				Response response = decodeResponse(buffer);
				message.addData(response);
				break;
			case NettyConstants.RESP_ASYNC_EVENT:
				Event event = decodeEvent(buffer);
				message.addData(event);
				break;
			default:
				break;
		}

		return message;
	}

	private String decodeString(ChannelBuffer buffer) {
		byte[] bytes = new byte[bodyLength];
		buffer.readBytes(bytes);
		String value = new String(bytes);

		return value;
	}
	
	private StreamID decodeStreamID(ChannelBuffer buffer) {
		byte[] bytes = new byte[this.bodyLength];
		buffer.readBytes(bytes);
		
		// Convert ByteArray to Object
		StreamID streamId = null;
		try {
			ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
			ObjectInputStream ois = new ObjectInputStream(bis);
			streamId = (StreamID) ois.readObject();
			ois.close();
			bis.close();
		} catch (IOException e) {
			System.out.println("IOException in Netty Message Decoder: ");
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			System.out.println("ClassNotFoundException in Netty Message Decoder: ");
			e.printStackTrace();
		}
		
		return streamId;
	}
	
	private Response decodeResponse(ChannelBuffer buffer) {
		byte[] bytes = new byte[this.bodyLength];
		buffer.readBytes(bytes);
		
		// Convert ByteArray to Object
		Response response = null;
		try {
			ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
			ObjectInputStream ois = new ObjectInputStream(bis);
			response = (Response) ois.readObject();
			ois.close();
			bis.close();
		} catch (IOException e) {
			System.out.println("IOException in Netty Message Decoder: ");
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			System.out.println("ClassNotFoundException in Netty Message Decoder: ");
			e.printStackTrace();
		}
		
		return response;
	}

	private Event decodeEvent(ChannelBuffer buffer) {
		byte[] bytes = new byte[this.bodyLength];
		buffer.readBytes(bytes);		
		
		// Convert ByteArray to Object
		Event event = null;
		try {
			ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
			ObjectInputStream ois = new ObjectInputStream(bis);
			event = (Event) ois.readObject();
			ois.close();
			bis.close();
		} catch (IOException e) {
			System.out.println("IOException in Netty Message Decoder: ");
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			System.out.println("ClassNotFoundException in Netty Message Decoder: ");
			e.printStackTrace();
		}
		
		return event;
	}
}
